<?php
/**
 * 数独生成类
 * @copyright (c) bbaa
 * Vesions V1.1
 * 生成算法思路来源:http://blog.csdn.net/liusaint1992/article/details/51147149
 * https://github.com/liusaint/games/blob/master/soduku/soduku.js移植
 */
class Sudoku {
	public $Sudoku_arr=Array();
	public $Sudoku_clone=Array();
	private $break=0;
	public function initarr() {
		$this->Sudoku_arr=Array();
		$this->Sudoku_clone=Array();
	}
	private function setThird($x,$y) {
		$numArr=array(1,2,3,4,5,6,7,8,9);
		shuffle($numArr);
		$cenNum=$x*10+$y;
		$thIndexArr =array($cenNum-11,$cenNum-1,$cenNum+9,$cenNum-10,$cenNum,$cenNum+10,$cenNum-9,$cenNum+1,$cenNum+11);
		for($a=0;$a<9;$a++){
			$this->Sudoku_arr[$thIndexArr[$a]]=$numArr[$a];
		}
	}
	private function GetXArr($j,$sdArr){
		//获取所在行的值。
		$arr = Array();
		for($a=1;$a<=9;$a++){
			if(isset($this->Sudoku_arr[$a*10+$j])){
				$arr[]=$sdArr[$a*10+$j];
			}
		}
		return $arr;
	}
	private function GetYArr($i,$sdArr){
		//获取所在列的值。
		$arr = Array();
		for($a=1;$a<=9;$a++){
			if(isset($sdArr[$i*10+$a])){
				$arr[]=$sdArr[$i*10+$a];
			}
		}
		return $arr;
	}
	private function GetThArr($i,$j,$sdArr){
		//获取所在三宫格的值。
		$arr = array();
		$cenNum = $this->getTh($i,$j);
		$thIndexArr = array($cenNum-11,$cenNum-1,$cenNum+9,$cenNum-10,$cenNum,$cenNum+10,$cenNum-9,$cenNum+1,$cenNum+11);
		for($a=0;$a<9;$a++){
			if(isset($sdArr[$thIndexArr[$a]])){
				$arr[]=$sdArr[$thIndexArr[$a]];
			}
		}
		return $arr;
	}
	private function getTh($i,$j){
		//获取所在三宫格的中间位坐标。
		$cenArr = array(22,52,82,25,55,85,28,58,88);
		$index = (ceil($j/3)-1) * 3 +ceil($i/3) -1;
		$cenNum = $cenArr[$index];
		return $cenNum;
	}
	private function arrMinus($arr1,$arr2){
		foreach($arr2 as $val) {
			Unset($arr1[(int)array_search($val,$arr1)]);
		}
		return $arr1;
	}
	public function GetSudoku_() {
		//重置数独
		$this->initarr();
		$this->setThird(2,2);
		$this->setThird(5,5);
		$this->setThird(8,8);
		$this->Sudoku_clone=$this->Sudoku_arr;
	}
	// 加载非数独终盘 并解 几乎不会解出[暴力无逻辑解]//
	public function load($arr,$self=false) {
		$this->initarr();
		if (!is_array($arr)) {
			if (strlen($arr)!=81) {
				return false;
			}
			$arr=$this->toArr($arr);
		} else {
			if (count($arr)!=81) {
				return false;
			}
		}
		var_dump( $arr);
		$this->Sudoku_clone=$arr;
		return $this->GetSudoku($self,false);
	}
	public function GetSudoku($self=false,$break=true) {
		//获取数独
		if(empty($this->Sudoku_clone)) {$this->GetSudoku_();} else {
			if (empty($this->Sudoku_arr)) {$this->Sudoku_arr=$this->Sudoku_clone;}
			if($break) {$this->break++;}
			if ($this->break>=15) {
				$this->break=0;
				unset($this->Sudoku_arr);
				$this->GetSudoku_();
			} else {
				unset($this->Sudoku_arr);
				$this->Sudoku_arr=$this->Sudoku_clone;
			}
		}
		$allNum = array(1,2,3,4,5,6,7,8,9);
		for($i=1;$i<=9;$i++){
			for($j=1;$j<=9;$j++){
				if(!isset($this->Sudoku_arr[$i*10+$j])||(int)$this->Sudoku_arr[$i*10+$j]==0) {
					$XArr = $this->getXArr($j,$this->Sudoku_arr);
					$YArr = $this->getYArr($i,$this->Sudoku_arr);
					$thArr = $this->getThArr($i,$j,$this->Sudoku_arr);
					$arr=array_unique(array_merge($XArr,$YArr,$thArr));
					unset($XArr,$YArr,$thArr);
					$ableArr=$this->arrMinus($allNum,$arr);
					unset($arr);
					shuffle($ableArr);
					if ((int)Count($ableArr)==0||empty($ableArr)||empty($ableArr[0])||$ableArr[0]=="") {unset($ableArr);$this->GetSudoku($self,$break);break;}
					$this->Sudoku_arr[$i*10+$j]=$ableArr[0];
					unset($ableArr);
					//if (empty($this->Sudoku_arr[$i*10+$j])||$this->Sudoku_arr[$i*10+$j]==null) {$this->GetSudoku();break;}
				}
			}
		}
		if (!$self) { 
			return $this->Sudoku_arr;
		} else {
			return $this;
		}
	}
	
	public function toString($arr = array()) {
		//数独数组转字符串
		if(empty($arr)) {
			$arr=$this->Sudoku_arr;
		}
		$String='';
		for($i=1;$i<=9;$i++){
			for($j=1;$j<=9;$j++){
				$String.=$arr[$i*10+$j];
			}
		}
		return $String;
	}
	public function Randomc($arr,$Count=30) {
		//随机扣数
		$Random=Array();
		for($Co=1;$Co<=$Count;$Co++) {
			$Ran=mt_rand(1,9)*10+rand(1,9);
			While (isset($Random[$Ran])) {
				$Ran=mt_rand(1,9)*10+mt_rand(1,9);
			}
			$Random[$Ran]=$Ran;
			$arr[$Ran]=0;
		}
		Return $arr;
	}
	public function toArr($str) {
		//数独字符串转数组
		$Arr=str_split($str,9);
		for($i=0;$i<=8;$i++){
			$Arr1[$i]=str_split($Arr[$i]);
		}
		for($i=1;$i<=9;$i++){
			for($j=1;$j<=9;$j++){
				$Sudoku_Arr[$i*10+$j]=(int)$Arr[$i-1][$j-1];
			}
		}
		return $Sudoku_Arr;
	}
	public function check($arr) {
		//检查数独
		if (!is_array($arr)) {
			if (strlen($arr)!=81) {return false;}
			$arr=$this->toArr($arr);
		} else {
			if (count($arr)!=81) {return false;}
		}
		for($i=1;$i<=9;$i++){
			$MathArrX=Array(1,2,3,4,5,6,7,8,9);
			$MathArrY=Array(1,2,3,4,5,6,7,8,9);
			for($j=1;$j<=9;$j++){
				unset($MathArrX[(int)array_search($arr[$i*10+$j],$MathArrX)]);
				unset($MathArrY[(int)array_search($arr[$j*10+$i],$MathArrY)]);
			}
			if (Count($MathArrX)!=0|Count($MathArrX)!=0) {$error=true;return false;break;}
		}
		$cenArr = array(22,52,82,25,55,85,28,58,88);
		for($a=0;$a<9;$a++) {
			$ZJarr=Array();
			$ZJArray=Array(1,2,3,4,5,6,7,8,9);
			$cenNum=$cenArr[$a];
			$thIndexArr = array($cenNum-11,$cenNum-1,$cenNum+9,$cenNum-10,$cenNum,$cenNum+10,$cenNum-9,$cenNum+1,$cenNum+11);
			for($b=0;$b<9;$b++){
				if(isset($arr[$thIndexArr[$b]])){
					$ZJarr[]=$arr[$thIndexArr[$b]];
				}
			}
			for ($c=0;$c<9;$c++) {
				unset($ZJArray[(int)array_search($ZJarr[$c],$ZJArray)]);
			}
			if (Count($ZJArray)!=0) {$error=true;break;}
		}
		if ($error) {return false;} else {return true;}
	}
	public function diff_sudoku($Sudoku_arr,$Sudoku_arr1) {
		if(is_array($Sudoku_arr)&&is_array($Sudoku_arr1)&&isset($Sudoku_arr[11])) {
			$Sudoku_arr=self::toString($Sudoku_arr);
			$Sudoku_arr1=self::toString($Sudoku_arr1);
		}
		for ($i=0;$i<81;$i++) {
			if ($Sudoku_arr[$i]==$Sudoku_arr1[$i]||$Sudoku_arr1[$i]==0) {
				Unset ($Sudoku_arr[$i],$Sudoku_arr1[$i]);
			}
		}
		if (Count($Sudoku_arr)==0) {return true;} else {return false;}
	}
}